package com.lagou.homework.annotation;

import com.lagou.homework.DefaultListableBeanFactory;
import com.lagou.homework.support.BeanDefinition;

import java.lang.annotation.Annotation;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @author boting.guo
 * @date 2021/9/19 14:23
 */

public class AnnotationBeanNameGenerator {

	private static final String COMPONENT_ANNOTATION_CLASSNAME = "com.lagou.homework.annotation.Component";

	private final Map<String, Set<String>> metaAnnotationTypesCache = new HashMap<>(16);

	public String generateBeanName(BeanDefinition beanDefinition) throws Exception {
		AnnotationMetadata metadata = beanDefinition.getMetadata();
		Set<String> types = metadata.getAnnotationTypes();
		String beanName = null;
		for (String type : types) {
			Properties attributes = metadata.attributesFor(type);
			if (attributes.size() > 0) {
				Set<String> metaTypes = this.metaAnnotationTypesCache.computeIfAbsent(type, key -> {
					List<Annotation> mergedAnnotations = AnnotationsScanner.getMergedAnnotations(metadata.getAnnotation(key).annotationType());
					return mergedAnnotations.isEmpty() ? Collections.emptySet() :
							mergedAnnotations.stream().map(annotation -> annotation.annotationType().getName())
									.collect(Collectors.toCollection(LinkedHashSet::new));
				});

				if (isStereotypeWithNameValue(type, metaTypes, attributes)) {
					Object value = attributes.get("value");
					if (value instanceof String) {
						String strVal = (String) value;
						if (strVal.length() > 0) {
							beanName = strVal;
						}
					}
				}
			}
		}

		if (beanName == null) {
			beanName = beanDefinition.getBeanClassName().toLowerCase(Locale.ROOT);
		}

		return beanName;
	}

	protected boolean isStereotypeWithNameValue(String annotationType, Set<String> metaAnnotationTypes, Properties attributes) {
		boolean isStereotype = annotationType.equals(COMPONENT_ANNOTATION_CLASSNAME) ||
				metaAnnotationTypes.contains(COMPONENT_ANNOTATION_CLASSNAME);

		return (isStereotype && attributes != null && attributes.containsKey("value"));
	}
}
